View Javadoc
1 /* 2 * Scope: a generic MVC framework. 3 * Copyright (c) 2000-2002, The Scope team 4 * All rights reserved. 5 * 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * Neither the name "Scope" nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR 27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 * 35 * 36 * $Id: XSLPage.java,v 1.7 2002/09/05 15:41:45 ludovicc Exp $ 37 */ 38 package org.scopemvc.view.servlet.xml; 39 40 41 import java.util.HashMap; 42 import java.util.Iterator; 43 import java.util.LinkedList; 44 import java.util.List; 45 import org.apache.commons.logging.Log; 46 import org.apache.commons.logging.LogFactory; 47 import org.scopemvc.core.PropertyManager; 48 import org.scopemvc.core.Selector; 49 import org.scopemvc.util.Debug; 50 import org.scopemvc.util.ScopeConfig; 51 import org.scopemvc.util.convertor.StringConvertor; 52 import org.scopemvc.util.convertor.StringConvertors; 53 import org.xml.sax.ContentHandler; 54 import org.scopemvc.view.servlet.ValidationFailure; 55 56 /*** 57 * <P> 58 * 59 * A concrete {@link AbstractXSLPage} that uses Scope's ModelManager 60 * implementations to serialise its entire bound model object to an XML 61 * document. A better strategy would be to use a more intelligent view that 62 * selectively serialises relevant parts of the model object. </P> <P> 63 * 64 * Handles circular references using the "ID" and "IDREF" pattern. </P> 65 * 66 * @author <A HREF="mailto:smeyfroi@users.sourceforge.net">Steve Meyfroidt</A> 67 * @created 05 September 2002 68 * @version $Revision: 1.7 $ $Date: 2002/09/05 15:41:45 $ 69 */ 70 public class XSLPage extends AbstractXSLPage { 71 72 /*** 73 * The prefix on form parameters to identify a property/value pair used to 74 * repopulate the View's bound model. A property/value pair looks like: 75 * <CODE> 76 * <PROPERTY_ID_PREFIX>ViewID<VIEW_ID_SUFFIX>SelectorDescription 77 * </CODE> 78 * 79 * @see #populateModel 80 */ 81 static char PROPERTY_ID_PREFIX; 82 83 private final static Log LOG = LogFactory.getLog(XSLPage.class); 84 85 /*** 86 * Optional setting for whether the SAX convertor should write all the 87 * property "paths" out when this view is streamed. Some views might want 88 * this if they send back form parameters like <CODE>{path}=newValue</CODE> 89 * for automatic repopulation back into the bound model object. 90 */ 91 protected boolean requiresPropertyDescriptions; 92 93 /*** 94 * TODO: describe of the Field 95 */ 96 protected ModelToXML xmlGenerator = new ModelToXML(); 97 98 99 /*** 100 * @param inViewID unique View ID for routing incoming Controls 101 * @param inXslURI the XSLT this View uses to transform its model objects 102 * after they convert to XML 103 */ 104 public XSLPage(String inViewID, String inXslURI) { 105 this(inViewID, inXslURI, false); 106 } 107 108 109 /*** 110 * @param inViewID unique View ID for routing incoming Controls 111 * @param inXslURI the XSLT this View uses to transform its model objects 112 * after they convert to XML 113 * @param inRequiresModelIds Does this view need the SAX convertor to write 114 * out property description attributes for properties? For repopulation 115 * back into the bound model via {@link #populateModel} 116 */ 117 public XSLPage(String inViewID, String inXslURI, boolean inRequiresModelIds) { 118 super(inViewID, inXslURI); 119 requiresPropertyDescriptions = inRequiresModelIds; 120 init(); 121 } 122 123 124 /*** 125 * <P> 126 * 127 * Interprets any form parameters like <CODE> 128 * <PROPERTY_ID_PREFIX>SelectorDescription 129 * </CODE> as [property_description, property_value] pairs </P> <P> 130 * 131 * Extracts the property_description from the form parameter key, then 132 * populates its model object using the String value. Any parameters treated 133 * this way are removed from the HashMap. </P> 134 * 135 * @param ioParameters form parameters to parse for [property_description, 136 * property_value] pairs, removing any processed pairs from the 137 * parameters before return 138 * @return PopulateModelFailedException that will be handled as a validation 139 * failure. 140 */ 141 public List populateModel(HashMap ioParameters) { 142 if (LOG.isDebugEnabled()) { 143 LOG.debug("populateModel: " + ioParameters); 144 } 145 146 List errors = new LinkedList(); 147 // collect ValidationFailures in here 148 List toRemove = new LinkedList(); 149 for (Iterator i = ioParameters.keySet().iterator(); i.hasNext(); ) { 150 Object o = i.next(); 151 if (Debug.ON) { 152 Debug.assertTrue(o instanceof String); 153 } 154 String parameterKey = (String) o; 155 if (LOG.isDebugEnabled()) { 156 LOG.debug("populateModel: " + parameterKey); 157 } 158 159 // Recognise prefixed form parameters for model population 160 if (parameterKey.charAt(0) == PROPERTY_ID_PREFIX) { 161 162 // get the form parameter value and remove from the parameter list 163 o = ioParameters.get(parameterKey); 164 // ... if multiple values take the first 165 if (o instanceof Object[]) { 166 if (Debug.ON) { 167 Debug.assertTrue(((Object[]) o).length > 0); 168 } 169 o = ((Object[]) o)[0]; 170 } 171 if (Debug.ON) { 172 Debug.assertTrue(o instanceof String); 173 } 174 String stringValue = (String) o; 175 176 // Mark it for removal (avoid concurrency problem with Iterator) 177 toRemove.add(o); 178 179 // find the property description 180 String propertyDescription = parameterKey.substring(1); 181 try { 182 populateBoundModelProperty(propertyDescription, stringValue); 183 } catch (Exception e) { 184 if (LOG.isDebugEnabled()) { 185 LOG.debug("populateModel: got an exception: " + e); 186 } 187 errors.add(new ValidationFailure(propertyDescription, stringValue, e)); 188 } 189 } 190 } 191 192 for (Iterator i = toRemove.iterator(); i.hasNext(); ) { 193 ioParameters.remove(i.next()); 194 } 195 196 if (errors.size() == 0) { 197 errors = null; 198 } 199 return errors; 200 } 201 202 203 /*** 204 * Do it like this so that we can pick up application-specific 205 * ScopeConfig... static initializers would happen before user got a chance 206 * to setup the custom config properties. 207 */ 208 protected void init() { 209 PROPERTY_ID_PREFIX = ScopeConfig.getChar("ServletFormParameter.propertyIDPrefix"); 210 if (PROPERTY_ID_PREFIX == 0) { 211 LOG.fatal("No propertyIDPrefix in config."); 212 } 213 } 214 215 216 /*** 217 * @param inContentHandler Drive this ContentHandler with the Model's SAX 218 * events. 219 * @throws Exception TODO: Describe the Exception 220 */ 221 protected void generateXMLDocument(ContentHandler inContentHandler) 222 throws Exception { 223 if (requiresPropertyDescriptions) { 224 xmlGenerator.modelToXML(getBoundModel(), inContentHandler, 225 new FullIDGenerator()); 226 } else { 227 xmlGenerator.modelToXML(getBoundModel(), inContentHandler, 228 new NoIDGenerator()); 229 } 230 } 231 232 233 /*** 234 * Use the property_description, property_value pair passed to set a 235 * property in the bound model to a new value. Use StringConvertor if 236 * available to convert from String to the property's native datatype. 237 * 238 * @param inPropertyDescription TODO: Describe the Parameter 239 * @param inValue TODO: Describe the Parameter 240 * @throws Exception on any failure 241 */ 242 protected void populateBoundModelProperty(String inPropertyDescription, String inValue) 243 throws Exception { 244 if (LOG.isDebugEnabled()) { 245 LOG.debug("populateBoundModelProperty: " + inPropertyDescription + ", " + inValue); 246 } 247 248 if (getBoundModel() == null) { 249 LOG.error("No bound model for: " + this); 250 return; 251 } 252 253 // Get the PropertyManager for the bound model 254 Object model = getBoundModel(); 255 PropertyManager manager = PropertyManager.getInstance(model); 256 if (Debug.ON) { 257 Debug.assertTrue(manager != null, "null manager"); 258 } 259 260 // Get the Selector from the property description 261 Selector selector = Selector.fromString(inPropertyDescription); 262 263 // Use StringConvertors if available, else just set(Selector, String) 264 StringConvertor convertor = StringConvertors.forClass(manager.getPropertyClass(model, selector)); 265 if (convertor != null) { 266 if (LOG.isDebugEnabled()) { 267 LOG.debug("populateProperty: got " + convertor.getClass()); 268 } 269 Object value = convertor.stringAsValue(inValue); 270 manager.set(model, selector, value); 271 } else { 272 if (LOG.isDebugEnabled()) { 273 LOG.debug("populateProperty: no StringConvertor "); 274 } 275 manager.set(model, selector, inValue); 276 } 277 } 278 } 279 280 final class NoIDGenerator extends PropertyIDGenerator { 281 282 /*** 283 * Gets the property ID 284 * 285 * @return The propertyID value 286 */ 287 public String getPropertyID() { 288 return null; 289 } 290 } 291 292 final class FullIDGenerator extends PropertyIDGenerator { 293 294 /*** 295 * Gets the property ID 296 * 297 * @return The propertyID value 298 */ 299 public String getPropertyID() { 300 if (currentPropertySelector == null) { 301 return null; 302 } 303 return XSLPage.PROPERTY_ID_PREFIX 304 + Selector.asString(currentPropertySelector); 305 } 306 }

This page was automatically generated by Maven